/*
 * main.c
 *
 *  Created on: 2015-5-15
 *      Author: likui
 */


#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/epoll.h>

#include "error.h"
#include "tcp_listen.h"
#include "thread_pool.h"
#include "time_wheel.h"

#define MAX_FD				1024
#define MAX_EVENT_NUMBER	1024
#define TIMESLOT			2

int user_count = 0;
int epollfd = 0;
int pipefd[2];
time_wheel_t  			*time_wheel = NULL;

/**
 * @brief 信号处理函数，向管道发送信号编号
 * @param sig
 */
void sig_handler(int sig)
{
	int save_errno = errno;
	int msg = sig;

	send(pipefd[1], (char *)&msg, 1, 0);

	errno = save_errno;
}

/**
 * @brief 注册信号
 * @param sig
 */
void addsig(int sig)
{
	struct sigaction	sa;
	memset(&sa, '\0', sizeof(sa));
	sa.sa_handler = sig_handler;
	sa.sa_flags |= SA_RESTART;
	sigfillset(&sa.sa_mask);
	if (sigaction(sig, &sa, NULL) != 0) {
		error_ret("sigaction error");
	}
}

/**
 * @brief 信号处理函数
 */
void timer_handler()
{
	time_wheel_tick(time_wheel);
	alarm(TIMESLOT);
}

int main(int argc, char **argv)
{
	int listenfd, thread_num, max_req, i, number, sockfd, ntime = 10;
	bool stop = false, timeout = false;
	socklen_t addrlen;
	thread_pool_t			*thread_pool = NULL;
	http_conn_t			*users = NULL;
	struct epoll_event	events[MAX_EVENT_NUMBER];

	// 创建监听套接字
	if (argc != 5) {
		error_quit("usage miniHttpd host port# thread# req#");
	}

	listenfd = tcp_listen(argv[1], argv[2],  NULL, &addrlen);

	thread_num = atoi(argv[3]);
	max_req = atoi(argv[4]);

	// 创建线程池并初始化，需要释放内存
	thread_pool = thread_pool_create(thread_num, max_req);
	if (thread_pool == NULL) {
		error_quit("thread_pool_create error");
	}

	// 预先为每个可能的客户连接分配一个http_conn_t，并初始化，需要释放内存
	if ( (users = (http_conn_t *)malloc(sizeof(http_conn_t) * MAX_FD) ) == NULL) {
		thread_pool_destroy(&thread_pool);
		error_sys("malloc error");
	}
	for ( i = 0; i < MAX_FD; i++) {
		http_conn_init_part(&users[i]);
	}

	// 创建epoll并将监听套接字listenfd加入监听列表
	epollfd = epoll_create(5);
	epoll_add(epollfd, listenfd, false);

	// 创建UNIX域套接字，用于通信
	socketpair(AF_UNIX, SOCK_STREAM, 0, pipefd);
	set_fl(pipefd[1], O_NONBLOCK);
	epoll_add(epollfd, pipefd[0], false);

	// 创建时间轮并初始化，需要释放内存
	time_wheel = time_wheel_create();

	/* 信号 */
	addsig(SIGALRM);
	addsig(SIGTERM);
	addsig(SIGQUIT);

	timeout = false;
	alarm(TIMESLOT);

	while (!stop) {
		number = epoll_wait(epollfd, events, MAX_EVENT_NUMBER, -1);
		if (number < 0 && errno != EINTR) {
			error_ret("epoll_wait error");
			break;
		}

		for (i = 0; i < number; i++) {

			sockfd = events[i].data.fd;

			if (sockfd == listenfd) {
				// 监听套接字
				int connfd;
				struct sockaddr cliaddr;
				socklen_t cliaddrlen = sizeof(cliaddr);
				tw_timer_t *timer = NULL;

				connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &cliaddrlen);
				if (connfd < 0) {
					error_ret("accept error");
					continue;
				}
				if (user_count >= MAX_FD) {
					error_msg("Internal server busy");
					continue;
				}

				http_conn_init(&users[connfd], connfd, &cliaddr, cliaddrlen, epollfd);

				// 添加一个定时器结构到时间槽
				timer = time_wheel_add(time_wheel, ntime);
				timer->cb_func = http_conn_close_conn;		// 回调函数
				timer->http_conn = &users[connfd];			// HTTP结构
				timer->epollfd = epollfd;					// epoll描述符

				users[connfd].timer = timer;

			} else if ( (sockfd == pipefd[0]) && (events[i].events & EPOLLIN)) {
				// 管道
				int ret;
				char signals[1024];

				ret = recv(pipefd[0], signals, sizeof(signals), 0);
				if (ret == -1) {
					continue;
				} else if (ret == 0) {
					continue;
				} else {
					int j;
					for (j = 0; j < ret; j++) {
						switch(signals[j]) {
							case SIGALRM:
							{
								timeout = true;

								break;
							}
							case SIGTERM:
							case SIGQUIT:
							{
								stop = true;
							}
						}
					}
				}
			} else if (events[i].events & (EPOLLRDHUP | EPOLLHUP | EPOLLERR)) {
				// TCP连接被对方关闭或者对方关闭了写操作 | 挂起 | 错误
				// 关闭连接，移出定时器
				tw_timer_t *timer;
				timer = users[sockfd].timer;

				http_conn_close_conn(&users[sockfd], true, epollfd);
				time_wheel_del_timer(time_wheel, timer);

			} else if (events[i].events &  EPOLLIN) {
				// 普通数据可读
				tw_timer_t *timer;
				timer = users[sockfd].timer;

				if (http_conn_read(&users[sockfd])) {

					if (timer)
						time_wheel_update_timer(time_wheel, timer, ntime);

					if (thread_pool_append(thread_pool, users + sockfd) == false) {
						error_ret("thread_pool_append error");
					}
					// 读取成功，需要更新定时器


				} else {
					// 对方关闭连接，移出定时器
					http_conn_close_conn(&users[sockfd], true, epollfd);
					time_wheel_del_timer(time_wheel, timer);
				}
			} else if (events[i].events & EPOLLOUT) {
				// 普通数据可写
				if (http_conn_write(&users[sockfd], epollfd)) {
					http_conn_close_conn(&users[sockfd], true, epollfd);
				}
			}
		}

		if (timeout) {
			timer_handler();
			timeout = false;
		}

	}
	error_msg("top server");
	close(epollfd);
	close(listenfd);

	time_wheel_destroy(&time_wheel);
	thread_pool_destroy(&thread_pool);
	free(users);

	exit(0);
}